#ifndef SERVIDOR_H
#define SERVIDOR_H
#include "Listener.h"
#include "ServerSocket.h"
#include "Mensaje.h"
#include "BDUsuario.h"
#include "Conexion.h"
#include "Partido.h"
#include "Protocolo.h"
#include "Constantes.h"

#include <vector>
#include <string>
#include <map>


#define ME 1   // MENSAJE ERROR
#define MW 2 // MENSAJE WARNING
#define MI 3   // MENSAJE INFORMATIVO

#define PATH_NIVELES "./modelo/archivos/niveles"

class Servidor: public Listener {
  public:
    explicit Servidor(t_puerto puerto);
    virtual ~Servidor();

    int autenticarMensaje(Mensaje &mensaje, Usuario &usuario);
    int atender(ServerSocket &socket);
    int iniciar();

    void loguear(int tipo_error, const std::string &mensaje);

    int crearUsuario(Mensaje &msg_user_attr, std::string &respuesta);
    bool usuarioAutenticado(int uid);

    bool usuarioAutenticado(std::string nombre, Usuario **auth_user);

    int autenticarUsuario(Mensaje &mensaje, Usuario **usuario,
                          std::string &respuesta);

    int getPartidosDisponibles(int nivel, Json::Value &partidos);

    int crearPartido(Usuario *usuario, int nivel, int cant_jugadores);

    int unirAPartido(Usuario *usuario, int id);

    int quitarDePartido(Usuario *usuario);

    int iniciarPartido(int idPartido);

    int responder(const char *respuesta);
    int responder(std::string mensaje_serial);

    Socket *getSocketAceptado();

    void apagar();

    void getInformacionNiveles(int hasta_nivel, Json::Value &niveles);

    static bool getNivel(int nivel, Json::Value &jsonNivel);
    static bool nivelExiste(int nivel);
    bool cargarNiveles();

  private:
    Mutex m_partidos;
    std::vector<Partido *> partidos;
    ServerSocket socket;
    Mutex m_usuarios;
    BDUsuario usuarios;
    /* Jugadores conectados */
    Mutex m_conexiones;
    std::vector<Conexion *> conexiones;
    Mutex m_log;

    HMAC firmador;

    Partido *getPartidoById(int id);

    int notificarRespuestaUnion(Usuario *usuario, int respuesta,  int idPartido,
                                std::string detalles);
    int notificarAgregarJugador(Usuario *usuario, Partido *partido);
    int notificarPartidoNuevo(Partido *partido);
    int notificarRemoverPartido(Partido *partido);
    int notificarAbandonoDeJugador(Partido *partido, Usuario *usuario);

    int notificarAClientesEnHome(Mensaje &mensaje, int nivel,
                                 Usuario *usuario_a_exceptuar = NULL);

    /* Informacion de los archivos X.json */
    static std::map<int, Json::Value *> niveles;


    bool getJsonDeArchivo(std::fstream &a_nivel, Json::Value &jsonNivel);
    bool abrirArchivo(std::string ruta_archivo, std::fstream &a_nivel);
};

#endif
